package com.ll.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.ll.common.to.SkuHasStockTo;
import com.ll.common.to.mq.OrderTo;
import com.ll.common.to.ware.WareLockTo;
import com.ll.common.utils.R;
import com.ll.gulimall.ware.constant.WareLockConstant;
import com.ll.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.ll.gulimall.ware.entity.WareOrderTaskEntity;
import com.ll.gulimall.ware.excetpion.NoStockException;
import com.ll.gulimall.ware.fegin.MemberFeignService;
import com.ll.gulimall.ware.fegin.OrderFeignService;
import com.ll.gulimall.ware.fegin.ProductFeignService;
import com.ll.gulimall.ware.service.WareOrderTaskDetailService;
import com.ll.gulimall.ware.service.WareOrderTaskService;
import com.ll.gulimall.ware.vo.*;
import com.rabbitmq.client.Channel;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.omg.SendingContext.RunTime;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ll.common.utils.PageUtils;
import com.ll.common.utils.Query;

import com.ll.gulimall.ware.dao.WareSkuDao;
import com.ll.gulimall.ware.entity.WareSkuEntity;
import com.ll.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();

        //skuId:
        //wareId:
        String skuId = (String) params.get("skuId");
        if (!StringUtils.isEmpty(skuId)) {
            wrapper.eq("sku_id", skuId);
        }
        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)) {
            wrapper.eq("ware_id", wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    WareOrderTaskService wareOrderTaskService;

    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    OrderFeignService orderFeignService;

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {

        //如果是有库存的话 则修改 没有则新增
        List<WareSkuEntity> wareSkuEntities = this.list(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (wareSkuEntities == null || wareSkuEntities.size() == 0) {
            //则新增操作
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            //远程调用查询SkuName
            //不回滚的方式1：加Catch 这样就不会抛出异常 则不会回滚
            // 方式2:TODO 高级篇才知道
            try {
                //发生异常则不需要回滚 为一个字段回滚没有意义
                R info = productFeignService.info(skuId);
                Map<String, Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
                wareSkuEntity.setSkuName((String) skuInfo.get("skuName"));

            } catch (Exception e) {

            }
            wareSkuEntity.setStockLocked(0);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setWareId(wareId);
            this.save(wareSkuEntity);
        } else {
            this.baseMapper.addStock(skuId, wareId, skuNum);
        }

    }

    /**
     * 根据SkuIds 查询该商品是否有库存
     *
     * @param skuIds
     * @return
     */
    @Override
    public List<SkuHasStockTo> hasStock(List<Long> skuIds) {
        List<SkuHasStockTo> collect = skuIds.stream().map(skuId -> {
            SkuHasStockTo skuHasStockTo = new SkuHasStockTo();
            skuHasStockTo.setSkuId(skuId);
            Long stock = baseMapper.hasStock(skuId);
            skuHasStockTo.setHasStock(stock != null && stock > 0);
            return skuHasStockTo;
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 计算运费
     *
     * @param addrId
     * @return
     */
    @Override
    public FareAndAddressVo freight(Long addrId) {
        //TODO 在这里业务式需要跟物流接口计算 为了简单随便计算了一个值
        FareAndAddressVo addressVo = new FareAndAddressVo();

        R info = memberFeignService.info(addrId);

        MemberReceiveAddressEntityVo memberReceiveAddress = info.getData(new TypeReference<MemberReceiveAddressEntityVo>() {
        }, "memberReceiveAddress");
        //拿到地址信息 截串手机号最后一位作为运费
        if (memberReceiveAddress != null) {
            String phone = memberReceiveAddress.getPhone();
            //15010010474
            String substring = phone.substring(10, phone.length());
            BigDecimal fare = new BigDecimal(substring);
            addressVo.setFare(fare);
            addressVo.setAddress(memberReceiveAddress);
            return addressVo;
        }
        return null;

    }

    /**
     * 锁定库存方法
     *
     * @param wareLockVo
     * @return
     */
    @Transactional
    @Override
    public Boolean skuLock(WareLockVo wareLockVo) {


        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(wareLockVo.getOrderSn());
        //保存库存工作单
        wareOrderTaskService.save(wareOrderTaskEntity);
        //锁定库存如果有一个没有锁主，则全部回滚
        List<CartItemVo> lockItems = wareLockVo.getLockItems();
        if (lockItems != null && lockItems.size() > 0) {
            //组装需要锁定的数据
            List<SkuLockWare> lockWares = lockItems.stream().map(item -> {
                SkuLockWare lockWare = new SkuLockWare();
                Long skuId = item.getSkuId();//商品Id
                lockWare.setSkuId(skuId);
                Integer count = item.getCount();//锁定数量
                lockWare.setNum(count);
                //查询商品在那个仓库有库存
                List<Long> wareIds = this.baseMapper.findSkuHashStock(skuId);
                lockWare.setWareIds(wareIds);
                return lockWare;
            }).collect(Collectors.toList());


            //遍历需要锁定的数据
            for (SkuLockWare lockWare : lockWares) {
                //是否锁定成功
                boolean lockStock = false;
                if (lockWare.getWareIds() == null || lockWare.getWareIds().size() == 0) {
                    //这个商品没有任何仓库的库存
                    throw new NoStockException("商品库存锁定失败");
                }
                for (Long wareId : lockWare.getWareIds()) {
                    //需要锁定的仓库Id
                    Long count = this.baseMapper.lockSkuStocked(lockWare.getSkuId(), lockWare.getNum(), wareId);
                    if (count == 1) {
                        //锁定成功 保存库存工作详情单数据 发送mq
                        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
                        detailEntity.setLockStatus(1);
                        detailEntity.setSkuId(lockWare.getSkuId());
                        detailEntity.setSkuNum(lockWare.getNum());
                        detailEntity.setTaskId(wareOrderTaskEntity.getId());
                        detailEntity.setWareId(wareId);
                        wareOrderTaskDetailService.save(detailEntity);
                        //发送Mq消息到延时队列
                        com.ll.common.vo.WareOrderTaskDetailEntity detailTo = new com.ll.common.vo.WareOrderTaskDetailEntity();
                        BeanUtils.copyProperties(detailEntity, detailTo);
                        WareLockTo wareLockTo = new WareLockTo();
                        wareLockTo.setId(wareOrderTaskEntity.getId());
                        wareLockTo.setDetailEntity(detailTo);
                        rabbitTemplate.convertAndSend("stock-event-exchange", "stock.lock", wareLockTo);
                        lockStock = true;

                        break;
                    }
                    //锁定失败 重试 锁定另一个仓库
                }
                if (!lockStock) {
                    //这个商品的所以仓库都没有库存
                    throw new NoStockException("商品库存锁定失败");
                }
            }
        }
        return true;
    }

    /**
     * 自动解锁库存
     *
     * @param message
     * @param lockTo
     * @param channel
     */
    @Override
    public void releaseWare(Message message, WareLockTo lockTo, Channel channel) {
        //拿到订单Id  查询该订单状态
        //如下状态 订单关闭 订单过期 没有该订单的情况回滚库存
        Long id = lockTo.getId();
        WareOrderTaskEntity orderTaskEntity = wareOrderTaskService.getById(id);

        //查询库存工作详情
        WareOrderTaskDetailEntity detail = wareOrderTaskDetailService.getById(lockTo.getDetailEntity().getId());
        //拿到订单号
        String orderSn = orderTaskEntity.getOrderSn();
        //调用远程方法查询该订单状态
        R r = orderFeignService.orderStatus(orderSn);
        if (r.getCode() == 0) {//远程调用成功
            OrderVo order = r.getData(new TypeReference<OrderVo>() {
            });
            //判断该订单状态
            if (order == null || order.getStatus() == 4) {  //需要回滚
                //必须是该工作单是锁定状态才可以解锁库存
                if (detail.getLockStatus() == WareLockConstant.WareLockEnum.WARE_LOCK_STATUS.getCode()) {
                    //回滚库存
                    this.baseMapper.releaseWare(detail.getSkuId(), detail.getWareId(), detail.getSkuNum());
                    //修改库存详详情单状态
                    WareOrderTaskDetailEntity updateEeatil = new WareOrderTaskDetailEntity();
                    updateEeatil.setId(detail.getId());
                    updateEeatil.setLockStatus(WareLockConstant.WareLockEnum.LOCK_RELEASE_STATUS.getCode());
                    wareOrderTaskDetailService.updateById(updateEeatil);
                }
            }
            //该条消息不需要处理 因为他有订单存在
        } else {
            //远程调用失败
            throw new RuntimeException("远程调用失败!");
        }


    }

    @Override
    public void releaseWare(Message message, OrderTo order, Channel channel) {
        //查看订单状态
        String orderSn = order.getOrderSn();
        R r = orderFeignService.orderStatus(orderSn);
        if (r.getCode() == 0) {
            OrderVo orderVo = r.getData(new TypeReference<OrderVo>() {
            });
            //查看该订单是否能被关闭
            if (orderVo == null || orderVo.getStatus() == 4) { //可以释放库存
                WareOrderTaskEntity taskEntity = wareOrderTaskService.getByOrderSn(orderSn);
                //根据订单号查询库存工作单 拿到库存详情单


                List<WareOrderTaskDetailEntity> entities = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                        .eq("task_id", taskEntity.getId()).
                                eq("lock_status", WareLockConstant.WareLockEnum.WARE_LOCK_STATUS.getCode()));

                //回滚库存 修改库存工作详情状态
                for (WareOrderTaskDetailEntity entity : entities) {
                    this.baseMapper.releaseWare(entity.getSkuId(), entity.getWareId(), entity.getSkuNum());
                }
                //修改库存详情单状态
                wareOrderTaskDetailService.updateByStakId(taskEntity.getId());
            }
        } else {
            //远程调用失败
            throw new RuntimeException("远程调用失败!");
        }
    }

    @Data
    class SkuLockWare {
        private Long skuId;
        private Integer num;
        private List<Long> wareIds;
    }


}